package com.tsyz.servlet.util;


import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT(JSON Web Token)工具类
 * 提供JWT的生成、验证和解析功能
 * <p>
 * 主要功能:
 * 1. 生成包含用户信息的JWT令牌
 * 2. 验证JWT令牌的有效性
 * 3. 从JWT令牌中提取用户信息
 * 4. 检查JWT令牌是否过期
 * @author ext.zhaoke14
 */
@Component  // 声明为Spring组件，由Spring容器管理
public class JwtUtil {

    /**
     * JWT签名密钥
     * 使用HS512算法所需的足够强度的密钥
     * Keys.secretKeyFor()方法会自动生成符合算法要求的安全密钥
     * <p>
     * 注意: 实际生产环境中应考虑:
     * 1. 从配置文件中读取密钥
     * 2. 使用非对称加密算法(如RS256)
     * 3. 定期轮换密钥
     */
    @Value("${tsyz.jwt.secret.key}")
    private String JWT_SECRET_KEY;

    /**
     * JWT令牌过期时间(毫秒)
     * 当前设置为7天(7 * 24 * 60 * 60 * 1000毫秒)
     * <p>
     * 注意: 应根据业务需求调整过期时间:
     * 1. 高安全性场景可设置较短过期时间
     * 2. 可考虑使用refresh token机制延长会话
     */
    @Value("${tsyz.jwt.secret.timeout}")
    private long EXPIRATION_TIME;

    /**
     * 生成JWT令牌
     *
     * @param userId   用户ID，将存储在JWT的claims中
     * @param userName 用户名，将作为JWT的subject
     * @return 生成的JWT令牌字符串
     */
    public String generateToken(Long userId, String userName) {
        // 创建JWT的claims(声明)，存储自定义数据
        Map<String, Object> claims = new HashMap<>();
        // 存储用户ID
        claims.put("userId", userId);
        // 存储用户名
        claims.put("userName", userName);

        // 使用Jwts构建器创建JWT
        return Jwts.builder()
                // 设置自定义claims
                .setClaims(claims)
                // 设置主题(通常是用户名)
                .setSubject(userName)
                // 设置签发时间
                .setIssuedAt(new Date(System.currentTimeMillis()))
                // 设置过期时间
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                // 使用密钥签名
                .signWith(getSecretKey(), SignatureAlgorithm.HS512)
                // 生成紧凑的URL安全字符串
                .compact();
    }


    /**
     * 获取SecretKey对象
     * 将配置的字符串密钥转换为安全的SecretKey对象
     */
    private SecretKey getSecretKey() {
        // 将配置的Base64编码字符串密钥转换为字节数组
        byte[] keyBytes = java.util.Base64.getDecoder().decode(JWT_SECRET_KEY);
        // 使用Keys.hmacShaKeyFor创建安全的密钥对象
        return Keys.hmacShaKeyFor(keyBytes);
    }


    /**
     * 验证JWT令牌的有效性
     *
     * @param token 待验证的JWT令牌
     * @return boolean 是否有效:
     * true - 令牌有效且未过期
     * false - 令牌无效或已过期
     */
    public Boolean validateToken(String token) {
        try {
            // 解析并验证JWT签名
            Jwts.parserBuilder()
                    // 设置签名密钥
                    .setSigningKey(getSecretKey()).build()
                    // 解析JWS(签名JWT)
                    .parseClaimsJws(token);
            // 检查令牌是否过期
            return !isTokenExpired(token);
        } catch (Exception e) {
            // 捕获所有异常(签名无效、格式错误等)
            return false;
        }
    }

    /**
     * 从JWT令牌中获取用户名(subject)
     *
     * @param token JWT令牌
     * @return String 用户名，解析失败返回null
     */
    public String getUserNameFromToken(String token) {
        try {
            // 解析JWT令牌获取claims
            Claims claims = Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token).getBody();

            // 返回subject(用户名)
            return claims.getSubject();
        } catch (Exception e) {
            // 解析失败返回null
            return null;
        }
    }

    /**
     * 从JWT令牌中获取用户ID
     *
     * @param token JWT令牌
     * @return Long 用户ID，解析失败返回null
     */
    public Long getUserIdFromToken(String token) {
        try {
            // 解析JWT令牌获取claims
            Claims claims = Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token).getBody();

            // 从claims中获取userId并转换为Long
            return Long.valueOf(claims.get("userId").toString());
        } catch (Exception e) {
            // 解析失败返回null
            return null;
        }
    }

    /**
     * 检查JWT令牌是否过期(私有方法)
     *
     * @param token JWT令牌
     * @return boolean 是否过期:
     * true - 已过期
     * false - 未过期
     */
    private Boolean isTokenExpired(String token) {
        try {
            // 解析JWT令牌获取claims
            Claims claims = Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token).getBody();

            // 获取过期时间并与当前时间比较
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            // 解析异常视为令牌已过期
            return true;
        }
    }
}